﻿using System;
using System.IO;
using System.Data;
using System.Configuration;
using System.Linq;
using System.Web;
using System.Web.Security;
using System.Web.Configuration;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Xml;
using System.Xml.Linq;
using System.Collections;
using System.Collections.Generic;
using System.ServiceModel.Syndication;
using System.Diagnostics;


namespace Microsoft.SqlCommunity.SQLRSS
{
    /// <summary>
    /// The class which represents the root of the tree of all objects created from the XML configuration file.  This is
    /// the interface point between the web service and the rest of the application which manages the configuration file 
    /// and acquires changes from the database.
    /// </summary>
    public class FeedMap : Description
    {
        // The path to the XML configuration file.  This is provided by the process which hosts the syndication web service. 
        public string ConfigurationFileName {get; set;}

        // The set of database connections
        public ProtectedDictionary<string, Connection, XElement> Connections { get; set; }
        public DateTime lastReadDateTime;

        private FileSystemWatcher fileWatcher;

        public FeedMap(string feedMapConfigurationFileName)
        {
            Logger.Current.LogMessage(EventLogEntryType.Information, "FeedMap service launched at {0}",
                DateTime.Now.ToString("F"));
            ConfigurationFileName = feedMapConfigurationFileName;
            this.Connections = new ProtectedDictionary<string, Connection, XElement>(null);
            lastReadDateTime = File.GetLastWriteTimeUtc(ConfigurationFileName);

            fileWatcher = new FileSystemWatcher(Path.GetDirectoryName(ConfigurationFileName),
                Path.GetFileName(ConfigurationFileName));
            fileWatcher.Changed += new FileSystemEventHandler(fileWatcher_Changed);


            XElement root = XElement.Load(ConfigurationFileName);

            if (root.Elements(fm + "Connection") != null)
            {
                this.Connections.LoadValues((element) => AttributeValueOrNull(element, "Name"),
                    (element) => new Connection(element),
                    root.Elements(fm + "Connection"));
            }

            fileWatcher.EnableRaisingEvents = true;
        }

        void fileWatcher_Changed(object sender, FileSystemEventArgs e)
        {
            RefreshFeedMap();
        }


        private void RefreshFeedMap()
        {
            if (File.GetLastWriteTimeUtc(ConfigurationFileName) != lastReadDateTime)
            {
                lastReadDateTime = File.GetLastWriteTimeUtc(ConfigurationFileName);
                IEnumerable<XElement> connectionElements = XElement.Load(ConfigurationFileName).Elements(fm + "Connection");
                Connections.MergeValues((element) => AttributeValueOrNull(element, "Name"),
                    (connectionElement, currentConnection) => Connection.MergeConnection(connectionElement, currentConnection),
                    connectionElements);
            }

        }

        public SyndicationFeed CurrentChanges(string connection, string table)
        {
            Connection dataConnection;
            if (Connections.TryLookupValue(connection, out dataConnection))
            {
                return dataConnection.ExecuteWithLock<SyndicationFeed>(LockType.Read, () =>  dataConnection.CurrentChanges(table));
            }
            else
                throw new ArgumentException(string.Format("Connection {0} not found.", connection), "connection");
        }
    }
}
